﻿// <copyright file="EntityCollection.cs" company="City of Portland, BES-ASM">
// </copyright>
// <summary>EntityCollection class</summary>

namespace BESASM.EMGAATS.BusinessClasses
{
  using System;
  using System.Collections;
  using System.Collections.Generic;
  using System.Collections.ObjectModel;
  using System.Collections.Specialized;
  using System.Linq;
  using System.Text;
  using System.Threading.Tasks;

  /// <summary>
  /// EntityCollection class
  /// </summary>
  /// <typeparam name="T">A class that implement IEntity</typeparam>
  public class EntityCollection<T> : KeyedCollection<int, T>, INotifyCollectionChanged where T : IEntity
  {
    private INetwork network = null;

    /// <summary>
    /// Initializes a new instance of the EntityCollection class.
    /// </summary>
    /// <param name="network">The network this entity collection belongs to.</param>
    public EntityCollection(INetwork network)
      : base(null, 0)
    {
      this.network = network;
    }

    /// <summary>
    /// Raised when the collection changes
    /// </summary>
    public event NotifyCollectionChangedEventHandler CollectionChanged;

    /// <summary>
    /// Changes the internal key for the selected entity
    /// </summary>
    /// <param name="entity">The entity for which to change the key.</param>
    /// <param name="newId">The new key for the entity (should match the entity.Id).</param>
    internal void ChangeKey(T entity, int newId)
    {
      this.ChangeItemKey(entity, newId);
    }

    /// <summary>
    /// Gets the key inside the collection for the provided item
    /// </summary>
    /// <param name="item">The item in question</param>
    /// <returns>The key for the item</returns>
    protected override int GetKeyForItem(T item)
    {
      return item.Id;
    }

    /// <summary>
    /// Inserts an entity into the EntityCollection at the specified index.
    /// </summary>
    /// <param name="index">The zero-based index at which entity should be inserted</param>
    /// <param name="item">The entity to insert.</param>
    protected override void InsertItem(int index, T item)
    {
      if (item.Network != null)
      {
        throw new ArgumentException("The entity already belongs to a network.");
      }

      base.InsertItem(index, item);

      item.Network = this.network;

      this.NotifyCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index));
    }

    /// <summary>
    /// Replaces the entity at the specified index with the specified entity.
    /// </summary>
    /// <param name="index">The zero-based index of the entity to be replaced.</param>
    /// <param name="item">The new entity.</param>
    protected override void SetItem(int index, T item)
    {
      if (item.Network != null)
      {
        throw new ArgumentException("The entity already belongs to a network.");
      }

      T replaced = this.Items[index];

      base.SetItem(index, item);

      item.Network = this.network;
      replaced.Network = null;

      this.NotifyCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, item, replaced, index));
    }

    /// <summary>
    /// Removes the entity at the specified index of the EntityCollection
    /// </summary>
    /// <param name="index">The zero-based index of the element to remove.</param>
    protected override void RemoveItem(int index)
    {
      T removed = this.Items[index];

      base.RemoveItem(index);

      removed.Network = null;

      this.NotifyCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, removed));
    }

    /// <summary>
    /// Removes all elements from the EntityCollection
    /// </summary>
    protected override void ClearItems()
    {
      IList<T> clearedItems = new List<T>(this.Items);

      base.ClearItems();

      foreach (T entity in clearedItems)
      {
        entity.Network = null;
      }

      this.NotifyCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, (IList)clearedItems));
    }

    /// <summary>
    /// Raises the CollectionChanged event.
    /// </summary>
    /// <param name="eventArgs">The event arguments detailing what items have been changed.</param>
    protected void NotifyCollectionChanged(NotifyCollectionChangedEventArgs eventArgs)
    {
      if (this.CollectionChanged != null)
      {
        this.CollectionChanged(this, eventArgs);
      }
    }
  }
}
